using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;

namespace H2.Sections
{
    public class Bitmaps
    {
        private MapStream Map;
        private List<string> PathCache;
        private List<BitmapCollection> BitmapCollectionsCache;

        internal Bitmaps(MapStream map)
        { Map = map; CreateCache(); }

        private void CreateCache()
        {
            PathCache = new List<string>(Map.Tags.GetStringArrayOfClass("bitm"));
            TagInfo[] tis = Map.Tags.GetTagInfoArrayOfClass("bitm");
            BitmapCollectionsCache = new List<BitmapCollection>(PathCache.Count);

            for (int i = 0; i < PathCache.Count; i++)
                BitmapCollectionsCache.Add(new BitmapCollection(Map, tis[i].Offset));
        }

        public BitmapCollection this[string Path] { get { return BitmapCollectionsCache[PathCache.IndexOf(Path)]; } set { BitmapCollectionsCache[PathCache.IndexOf(Path)] = value; } }

        public System.Drawing.Bitmap LoadPreviewImage(string Path, int Chunk, BinaryReader MainMenu, BinaryReader Shared, BinaryReader SpShared)
        {
            int i = PathCache.IndexOf(Path);
            if (i != -1)
                return BitmapCollectionsCache[i][Chunk].LoadPreview(MainMenu, Shared, SpShared);
            else
                return null;
        }

        public struct RGBA_IntColor
        {
            public int R, G, B, A;
        }

        public struct RGBA_ByteColor
        {
            public byte R, G, B, A;
        }

        private static RGBA_IntColor ShortToRGBA(int color)
        {
            RGBA_IntColor rcs;
            color = Convert.ToUInt16(color);
            rcs.R = (((color >> 11) & 31) * 255) / 31;
            rcs.G = (((color >> 5) & 63) * 255) / 63;
            rcs.B = (((color >> 0) & 31) * 255) / 31;
            rcs.A = 255;
            return rcs;
        }

        private static int RGBAToInt(RGBA_IntColor rcs)
        {
            return (rcs.A << 24) | (rcs.R << 16) | (rcs.G << 8) | rcs.B;
        }

        private static RGBA_IntColor GradientColors(RGBA_IntColor Col1, RGBA_IntColor Col2)
        {
            RGBA_IntColor ret;
            ret.R = ((Col1.R * 2 + Col2.R)) / 3;
            ret.G = ((Col1.G * 2 + Col2.G)) / 3;
            ret.B = ((Col1.B * 2 + Col2.B)) / 3;
            ret.A = 255;
            return ret;
        }

        private static RGBA_IntColor GradientColorsHalf(RGBA_IntColor Col1, RGBA_IntColor Col2)
        {
            RGBA_IntColor ret;
            ret.R = (Col1.R / 2 + Col2.R / 2);
            ret.G = (Col1.G / 2 + Col2.G / 2);
            ret.B = (Col1.B / 2 + Col2.B / 2);
            ret.A = 255;
            return ret;
        }

        public static RGBA_ByteColor[] H2Palette;
        public static bool H2PaletteLoaded = false;
        public static void LoadHalo2Palette()
        {
            #region H2Palette

            byte[] H2PaletteBuffer = new byte[] {
                255, 126, 126, 255, 255, 127, 126, 255, 255, 128, 126, 255, 255, 129, 126, 255, 255, 126, 
                127, 255, 255, 127, 127, 255, 255, 128, 127, 255, 255, 129, 127, 255, 255, 126, 128, 255, 
                255, 127, 128, 255, 255, 128, 128, 255, 255, 129, 128, 255, 255, 126, 129, 255, 255, 127, 
                129, 255, 255, 128, 129, 255, 255, 129, 129, 255, 255, 130, 127, 255, 255, 127, 131, 255, 
                255, 127, 125, 255, 255, 131, 129, 255, 255, 124, 129, 255, 255, 130, 124, 255, 255, 129, 
                132, 255, 255, 124, 125, 255, 255, 133, 127, 255, 255, 125, 132, 255, 255, 128, 122, 255, 
                255, 132, 132, 255, 255, 122, 128, 255, 255, 133, 124, 255, 255, 127, 135, 255, 255, 124, 
                122, 255, 255, 136, 130, 255, 255, 121, 132, 255, 255, 131, 120, 255, 255, 132, 136, 255, 
                255, 119, 124, 255, 255, 137, 125, 255, 255, 123, 137, 255, 255, 125, 118, 255, 255, 137, 
                134, 255, 255, 117, 130, 255, 255, 135, 119, 255, 255, 129, 140, 255, 255, 119, 120, 255, 
                255, 141, 128, 255, 255, 119, 137, 255, 255, 129, 115, 255, 255, 136, 139, 255, 255, 114, 
                126, 255, 255, 140, 120, 255, 255, 124, 142, 255, 255, 121, 115, 255, 255, 142, 133, 255, 
                255, 113, 134, 255, 254, 135, 113, 255, 254, 133, 144, 255, 254, 113, 120, 255, 254, 145, 
                124, 255, 254, 118, 142, 255, 254, 126, 110, 255, 254, 142, 140, 255, 254, 109, 129, 255, 
                254, 142, 114, 255, 254, 127, 147, 255, 254, 115, 113, 255, 254, 148, 131, 255, 254, 111, 
                140, 255, 254, 133, 107, 255, 254, 139, 147, 255, 254, 107, 121, 255, 254, 148, 119, 255, 
                253, 119, 149, 255, 253, 120, 106, 255, 253, 149, 139, 255, 253, 105, 134, 255, 253, 141, 
                108, 255, 253, 132, 152, 255, 253, 108, 113, 255, 253, 153, 126, 255, 253, 111, 147, 255, 
                253, 128, 102, 255, 253, 146, 147, 255, 253, 101, 126, 255, 253, 150, 111, 255, 252, 123, 
                155, 255, 252, 113, 104, 255, 252, 155, 135, 255, 252, 103, 141, 255, 252, 138, 101, 255, 
                252, 139, 155, 255, 252, 101, 115, 255, 252, 157, 119, 255, 252, 113, 155, 255, 252, 121, 
                98, 255, 252, 154, 146, 255, 251, 96, 132, 255, 251, 149, 103, 255, 251, 129, 161, 255, 
                251, 105, 105, 255, 251, 161, 129, 255, 251, 102, 150, 255, 251, 132, 94, 255, 251, 148, 
                156, 255, 251, 94, 120, 255, 251, 159, 110, 255, 250, 117, 162, 255, 250, 113, 95, 255, 
                250, 162, 142, 255, 250, 93, 141, 255, 250, 145, 95, 255, 250, 138, 164, 255, 250, 96, 
                108, 255, 250, 166, 121, 255, 249, 104, 159, 255, 249, 125, 89, 255, 249, 157, 155, 255, 
                249, 88, 128, 255, 249, 158, 101, 255, 249, 124, 169, 255, 249, 103, 95, 255, 248, 169, 
                135, 255, 248, 92, 151, 255, 248, 139, 87, 255, 248, 148, 166, 255, 248, 87, 113, 255, 
                248, 168, 111, 255, 248, 109, 168, 255, 247, 115, 86, 255, 247, 167, 150, 255, 247, 84, 
                138, 255, 247, 154, 91, 255, 247, 134, 174, 255, 247, 92, 98, 255, 247, 175, 126, 255, 
                246, 94, 162, 255, 246, 130, 80, 255, 246, 159, 165, 255, 246, 80, 122, 255, 246, 168, 
                100, 255, 246, 117, 176, 255, 245, 103, 85, 255, 245, 176, 143, 255, 245, 82, 149, 255, 
                245, 148, 81, 255, 245, 146, 176, 255, 244, 82, 104, 255, 244, 178, 114, 255, 244, 100, 
                172, 255, 244, 119, 76, 255, 244, 170, 161, 255, 244, 74, 133, 255, 243, 165, 88, 255, 
                243, 128, 183, 255, 243, 91, 87, 255, 243, 183, 133, 255, 243, 84, 162, 255, 242, 138, 
                73, 255, 242, 158, 176, 255, 242, 73, 113, 255, 242, 179, 101, 255, 242, 108, 182, 255, 
                241, 106, 74, 255, 241, 181, 153, 255, 241, 72, 146, 255, 241, 158, 76, 255, 240, 141, 
                187, 255, 240, 79, 93, 255, 240, 188, 120, 255, 240, 89, 175, 255, 240, 125, 66, 255, 
                239, 172, 172, 255, 239, 66, 125, 255, 239, 176, 88, 255, 239, 120, 191, 255, 238, 92, 
                76, 255, 238, 191, 142, 255, 238, 72, 160, 255, 238, 148, 66, 255, 237, 156, 187, 255, 
                237, 67, 103, 255, 237, 190, 105, 255, 237, 97, 187, 255, 237, 111, 63, 255, 236, 185, 
                164, 255, 236, 61, 140, 255, 236, 170, 74, 255, 235, 134, 196, 255, 235, 77, 81, 255, 
                235, 197, 128, 255, 235, 77, 175, 255, 234, 134, 58, 255, 234, 171, 184, 255, 234, 58, 
                116, 255, 234, 188, 90, 255, 233, 109, 197, 255, 233, 95, 64, 255, 233, 196, 153, 255, 
                233, 61, 156, 255, 232, 159, 62, 255, 232, 150, 198, 255, 232, 64, 91, 255, 231, 201, 
                112, 255, 231, 85, 189, 255, 231, 118, 53, 255, 231, 186, 177, 255, 230, 52, 131, 255, 
                230, 182, 74, 255, 230, 125, 205, 255, 229, 78, 69, 255, 229, 205, 138, 255, 229, 64, 
                173, 255, 228, 145, 51, 255, 228, 167, 196, 255, 228, 52, 104, 255, 227, 200, 94, 255, 
                227, 97, 202, 255, 227, 101, 52, 255, 227, 200, 165, 255, 226, 49, 149, 255, 226, 172, 
                59, 255, 226, 142, 209, 255, 225, 63, 78, 255, 225, 211, 121, 255, 225, 72, 189, 255, 
                224, 128, 44, 255, 224, 185, 190, 255, 224, 44, 121, 255, 223, 195, 76, 255, 223, 113, 
                212, 255, 223, 82, 56, 255, 222, 211, 150, 255, 222, 51, 168, 255, 221, 158, 47, 255, 
                221, 161, 209, 255, 221, 49, 91, 255, 220, 212, 102, 255, 220, 84, 204, 255, 220, 109, 
                41, 255, 219, 201, 179, 255, 219, 39, 140, 255, 219, 186, 59, 255, 218, 132, 218, 255, 
                218, 64, 64, 255, 217, 219, 132, 255, 217, 58, 187, 255, 217, 140, 37, 255, 216, 181, 
                203, 255, 216, 38, 108, 255, 216, 208, 82, 255, 215, 100, 217, 255, 215, 89, 43, 255, 
                214, 215, 164, 255, 214, 39, 160, 255, 214, 172, 44, 255, 255, 128, 128, 0, };

            #endregion

            H2Palette = new RGBA_ByteColor[256];
            for (int x = 0; x < 256; x++)
            {
                int r = x * 4;
                H2Palette[x].R = H2PaletteBuffer[r];
                H2Palette[x].G = H2PaletteBuffer[r + 1];
                H2Palette[x].B = H2PaletteBuffer[r + 2];
                H2Palette[x].A = H2PaletteBuffer[r + 3];
            }

            H2PaletteLoaded = true;
        }

        public static byte[] DecodeDXT1(int width, int height, byte[] SourceData)
        {
            RGBA_IntColor[] Color = new RGBA_IntColor[5];

            RGBA_IntColor CColor;
            RGBA_IntColor zeroColor;

            int CData;
            int c1;
            int c2;
            int dptr = 0;

            bool trans;
            byte[] DestData = new byte[(width * height) * 4];

            int ChunksPerHLine = width / 4;
            if (ChunksPerHLine == 0) ChunksPerHLine = 1;

            for (int i = 0; i < (width * height); i += 16)
            {
                c1 = (SourceData[dptr + 1] << 8) | (SourceData[dptr]);
                c2 = (SourceData[dptr + 3] << 8) | (SourceData[dptr + 2]);

                trans = (!(c1 > c2));

                Color[0] = ShortToRGBA(c1);
                Color[1] = ShortToRGBA(c2);

                if (!trans)
                {
                    Color[2] = GradientColors(Color[0], Color[1]);
                    Color[3] = GradientColors(Color[1], Color[0]);
                }
                else
                {
                    zeroColor = Color[0];
                    Color[2] = GradientColorsHalf(Color[0], Color[1]);
                    Color[3] = zeroColor;
                }

                CData = (SourceData[dptr + 4] << 0) | (SourceData[dptr + 5] << 8) |
                    (SourceData[dptr + 6] << 16) | (SourceData[dptr + 7] << 24);

                int ChunkNum = i / 16;
                long XPos = ChunkNum % ChunksPerHLine;
                long YPos = (ChunkNum - XPos) / ChunksPerHLine;

                long tmp1, tmp2;

                int sizeh = height < 4 ? height : 4;
                int sizew = width < 4 ? width : 4;

                int x, y;
                for (x = 0; x < sizeh; x++)
                {
                    for (y = 0; y < sizew; y++)
                    {
                        CColor = Color[CData & 3];
                        CData >>= 2;
                        tmp1 = ((YPos * 4 + x) * width + XPos * 4 + y) * 4;
                        tmp2 = RGBAToInt(CColor);
                        DestData[tmp1] = Convert.ToByte(CColor.B);
                        DestData[tmp1 + 1] = Convert.ToByte(CColor.G);
                        DestData[tmp1 + 2] = Convert.ToByte(CColor.R);
                        DestData[tmp1 + 3] = Convert.ToByte(CColor.A);
                    }
                }
                dptr += 8;
            }
            return DestData;
        }

        public static byte[] DecodeDXT23(int width, int height, byte[] SourceData)
        {
            RGBA_IntColor[] Color = new RGBA_IntColor[5];

            RGBA_IntColor CColor;
            RGBA_IntColor c1, c2, c3, c4;
            int CData;
            byte[] DestData = new byte[(width * height) * 4];

            int ChunksPerHLine = width / 4;
            if (ChunksPerHLine == 0) ChunksPerHLine = 1;

            for (int i = 0; i < (width * height); i += 16)
            {
                c1 = ShortToRGBA((SourceData[i + 8]) | (SourceData[i + 9] << 8));
                c2 = ShortToRGBA((SourceData[i + 10]) | (SourceData[i + 11] << 8));
                c3 = GradientColors(Color[0], Color[1]);
                c4 = GradientColors(Color[1], Color[0]);
                Color[0] = c1;
                Color[1] = c2;
                Color[2] = c3;
                Color[3] = c4;

                CData = (SourceData[i + 12] << 0) | (SourceData[i + 13] << 8) | (SourceData[i + 14] << 16) | (SourceData[i + 15] << 24);

                int ChunkNum = i / 16;
                long XPos = ChunkNum % ChunksPerHLine;
                long YPos = (ChunkNum - XPos) / ChunksPerHLine;

                long ttmp;

                int alpha;
                int sizeh = height < 4 ? height : 4;
                int sizew = width < 4 ? width : 4;
                int x, y;

                for (x = 0; x < sizeh; x++)
                {
                    alpha = SourceData[i + (2 * x)] | (SourceData[i + (2 * x) + 1]) << 8;
                    for (y = 0; y < sizew; y++)
                    {
                        CColor = Color[CData & 3];
                        CData >>= 2;
                        CColor.A = (alpha & 15) * 16;
                        alpha >>= 4;
                        ttmp = ((YPos * 4 + x) * width + XPos * 4 + y) * 4;

                        DestData[ttmp] = (byte)CColor.B;
                        DestData[ttmp + 1] = (byte)CColor.G;
                        DestData[ttmp + 2] = (byte)CColor.R;
                        DestData[ttmp + 3] = (byte)CColor.A;
                    }
                }
            }
            return DestData;
        }

        public static byte[] DecodeDXT45(int width, int height, byte[] SourceData)
        {
            RGBA_IntColor[] Color = new RGBA_IntColor[4];
            RGBA_IntColor CColor;

            int CData;
            byte[] DestData = new byte[(width * height) * 4];

            int ChunksPerHLine = width / 4;
            if (ChunksPerHLine == 0) ChunksPerHLine = 1;

            for (int i = 0; i < (width * height); i += 16)
            {
                Color[0] = ShortToRGBA(SourceData[i + 8] | (SourceData[i + 9] << 8));
                Color[1] = ShortToRGBA(SourceData[i + 10] | (SourceData[i + 11] << 8));
                Color[2] = GradientColors(Color[0], Color[1]);
                Color[3] = GradientColors(Color[1], Color[0]);

                CData = (SourceData[i + 12] << 0) | (SourceData[i + 13] << 8) | (SourceData[i + 14] << 16) | (SourceData[i + 15] << 24);

                byte[] Alpha = new byte[8];
                Alpha[0] = SourceData[i];
                Alpha[1] = SourceData[i + 1];

                //Do the alphas
                if (Alpha[0] > Alpha[1])
                {
                    // 8-alpha block:  derive the other six alphas.
                    // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.

                    Alpha[2] = (byte)((6 * Alpha[0] + 1 * Alpha[1] + 3) / 7); // bit code 010
                    Alpha[3] = (byte)((5 * Alpha[0] + 2 * Alpha[1] + 3) / 7); // bit code 011
                    Alpha[4] = (byte)((4 * Alpha[0] + 3 * Alpha[1] + 3) / 7); // bit code 100
                    Alpha[5] = (byte)((3 * Alpha[0] + 4 * Alpha[1] + 3) / 7); // bit code 101
                    Alpha[6] = (byte)((2 * Alpha[0] + 5 * Alpha[1] + 3) / 7); // bit code 110
                    Alpha[7] = (byte)((1 * Alpha[0] + 6 * Alpha[1] + 3) / 7); // bit code 111
                }
                else
                {
                    // 6-alpha block.
                    // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
                    Alpha[2] = (byte)((4 * Alpha[0] + 1 * Alpha[1] + 2) / 5); // Bit code 010
                    Alpha[3] = (byte)((3 * Alpha[0] + 2 * Alpha[1] + 2) / 5); // Bit code 011
                    Alpha[4] = (byte)((2 * Alpha[0] + 3 * Alpha[1] + 2) / 5); // Bit code 100
                    Alpha[5] = (byte)((1 * Alpha[0] + 4 * Alpha[1] + 2) / 5); // Bit code 101
                    Alpha[6] = 0;            // Bit code 110
                    Alpha[7] = 255;          // Bit code 111
                }

                // Byte	Alpha
                // 0	Alpha_0
                // 1	Alpha_1 
                // 2	(0)(2) (2 LSBs), (0)(1), (0)(0)
                // 3	(1)(1) (1 LSB), (1)(0), (0)(3), (0)(2) (1 MSB)
                // 4	(1)(3), (1)(2), (1)(1) (2 MSBs)
                // 5	(2)(2) (2 LSBs), (2)(1), (2)(0)
                // 6	(3)(1) (1 LSB), (3)(0), (2)(3), (2)(2) (1 MSB)
                // 7	(3)(3), (3)(2), (3)(1) (2 MSBs)
                // (0

                // Read an int and a short
                int tmpword = SourceData[i + 2] | (SourceData[i + 3] << 8);
                long tmpdword = SourceData[i + 4] | (SourceData[i + 5] << 8) | (SourceData[i + 6] << 16) | (SourceData[i + 7] << 24);

                long alphaDat = tmpword | (tmpdword << 16);

                int ChunkNum = i / 16;
                long XPos = ChunkNum % ChunksPerHLine;
                long YPos = (ChunkNum - XPos) / ChunksPerHLine;
                long ttmp;
                int sizeh = height < 4 ? height : 4;
                int sizew = width < 4 ? width : 4;
                int x, y;
                for (x = 0; x < sizeh; x++)
                {
                    for (y = 0; y < sizew; y++)
                    {
                        CColor = Color[CData & 3];
                        CData >>= 2;
                        CColor.A = Alpha[alphaDat & 7];
                        alphaDat >>= 3;
                        ttmp = ((YPos * 4 + x) * width + XPos * 4 + y) * 4;
                        DestData[ttmp] = (byte)CColor.B;
                        DestData[ttmp + 1] = (byte)CColor.G;
                        DestData[ttmp + 2] = (byte)CColor.R;
                        DestData[ttmp + 3] = (byte)CColor.A;
                    }
                }
            }
            return DestData;
        }

        public static byte[] Swizzle(byte[] raw, int pixOffset, int width, int height, int depth, int bitCount, bool deswizzle)
        {
            bitCount /= 8;
            int a = 0;
            int b = 0;
            byte[] dataArray = new byte[raw.Length]; //width * height * bitCount;

            MaskSet masks = new MaskSet(width, height, depth);
            pixOffset = 0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (deswizzle)
                    {
                        a = ((y * width) + x) * bitCount;
                        b = (Swizzle(x, y, -1, masks)) * bitCount;
                    }
                    else
                    {
                        b = ((y * width) + x) * bitCount;
                        a = (Swizzle(x, y, -1, masks)) * bitCount;
                    }

                    if (a < dataArray.Length && b < raw.Length)
                    {
                        for (int i = pixOffset; i < bitCount + pixOffset; i++)
                            dataArray[a + i] = raw[b + i];
                    }
                    else return null;
                }
            }

            //for(int u = 0; u < offset; u++)
            //data[u] = raw[u];
            //for(int v = offset + (height * width * depth * bitCount); v < data.Length; v++)
            //	data[v] = raw[v];

            return dataArray;
        }

        public static byte[] Swizzle(byte[] raw, int width, int height, int depth, int bitCount, bool deswizzle)
        {
            return Swizzle(raw, 0, width, height, depth, bitCount, deswizzle);
        }

        private static int Swizzle(int x, int y, int z, MaskSet masks)
        {
            return SwizzleAxis(x, masks.x) | SwizzleAxis(y, masks.y) | (z == -1 ? 0 : SwizzleAxis(z, masks.z));
        }

        private static int SwizzleAxis(int val, int mask)
        {
            int bit = 1;
            int result = 0;

            while (bit <= mask)
            {
                int tmp = mask & bit;

                if (tmp != 0) result |= (val & bit);
                else val <<= 1;

                bit <<= 1;
            }

            return result;
        }

        private class MaskSet
        {
            public int x = 0;
            public int y = 0;
            public int z = 0;

            public MaskSet(int w, int h, int d)
            {
                int bit = 1;
                int index = 1;

                while (bit < w || bit < h || bit < d)
                {
                    //if (bit == 0) break;
                    if (bit < w)
                    {
                        x |= index;
                        index <<= 1;
                    }
                    if (bit < h)
                    {
                        y |= index;
                        index <<= 1;
                    }
                    if (bit < d)
                    {
                        z |= index;
                        index <<= 1;
                    }
                    bit <<= 1;
                }
            }
        }
    }
}
